﻿using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;

namespace LooseChart
{
    public abstract class IAxis : FrameworkElement
    {
        #region 绘制内容
        protected DrawingVisual DV { get; } = new DrawingVisual();
        protected override int VisualChildrenCount => 1;
        protected override Visual GetVisualChild(int index)
        {
            return DV;
        }
        #endregion

        public IAxis()
        {
            this.AddVisualChild(DV);
            this.Loaded += (sender, e) => { RedrawAll(); };
        }

        /// <summary>
        /// 轴类型/X、Y
        /// </summary>
        public AxisType AxisType
        {
            get { return (AxisType)GetValue(AxisTypeProperty); }
            set { SetValue(AxisTypeProperty, value); }
        }
        public static readonly DependencyProperty AxisTypeProperty = DependencyProperty.Register("AxisType", typeof(AxisType), typeof(IAxis), new PropertyMetadata(AxisType.X, (d, e) => { if (d is IAxis axis) axis.Draw(); }));


        #region 样式
        /// <summary>
        /// 前景色
        /// </summary>
        public Brush Foreground
        {
            get { return (Brush)GetValue(ForegroundProperty); }
            set { SetValue(ForegroundProperty, value); }
        }
        public static readonly DependencyProperty ForegroundProperty = DependencyProperty.Register("Foreground", typeof(Brush), typeof(IAxis), new PropertyMetadata(Brushes.Black, (d, e) =>
        {
            if (d is IAxis axis)
            {
                axis.LinePen = new Pen(e.NewValue as Brush, 1d);
                axis.StringPen = new Pen(e.NewValue as Brush, 0.1d);
                axis.Draw();
            }
        }));
        /// <summary>
        /// 背景色
        /// </summary>
        public Brush Background
        {
            get { return (Brush)GetValue(BackgroundProperty); }
            set { SetValue(BackgroundProperty, value); }
        }
        public static readonly DependencyProperty BackgroundProperty = DependencyProperty.Register("Background", typeof(Brush), typeof(IAxis), new PropertyMetadata(Brushes.Transparent, (d, e) => { if (d is IAxis axis) axis.Draw(); }));

        /// <summary>
        /// 字体大小
        /// </summary>
        public double FontSize
        {
            get { return (double)GetValue(FontSizeProperty); }
            set { SetValue(FontSizeProperty, value); }
        }
        public static readonly DependencyProperty FontSizeProperty = DependencyProperty.Register("FontSize", typeof(double), typeof(IAxis), new PropertyMetadata(12d, (d, e) => { if (d is IAxis axis) axis.Draw(); }));

        /// <summary>
        /// 指数字体大小
        /// </summary>
        public double PowerFontSize
        {
            get { return (double)GetValue(PowerFontSizeProperty); }
            set { SetValue(PowerFontSizeProperty, value); }
        }
        public static readonly DependencyProperty PowerFontSizeProperty = DependencyProperty.Register("PowerFontSize", typeof(double), typeof(IAxis), new PropertyMetadata(8d, (d, e) => { if (d is IAxis axis) axis.Draw(); }));
        #endregion

        #region 数据计算相关
        private static AxisRange DefaultRange = new AxisRange() { Min = 0d, Max = 100000d };
        public AxisRange Range
        {
            get { return (AxisRange)GetValue(RangeProperty); }
            set { SetValue(RangeProperty, value); }
        }
        public static readonly DependencyProperty RangeProperty = DependencyProperty.Register("Range", typeof(AxisRange), typeof(IAxis), new PropertyMetadata(DefaultRange, (d, e) =>
        {
            if (d is IAxis axis) axis.RedrawAll();
        }));

        /// <summary>
        /// 最大值
        /// </summary>
        internal virtual double Max
        {
            get { return double.IsNaN(Range.Max) || double.IsInfinity(Range.Max) ? DefaultRange.Max : Range.Max; }
        }

        /// <summary>
        /// 最小值
        /// </summary>
        internal virtual double Min
        {
            get { return double.IsNaN(Range.Min) || double.IsInfinity(Range.Min) ? DefaultRange.Min : Range.Min; }
        }

        #endregion

        #region 点位图形值，实际数值，实际坐标值相互转换
        /// <summary>
        /// 获取实际值和坐标值转换参数
        /// </summary>
        /// <returns></returns>
        public virtual ValueAxisConvertParamBase GetValueAxisConvertParam()
        {
            return null;
        }

        /// <summary>
        /// 实际值转换成坐标值
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public abstract double ValueToAxisValue(double value, ValueAxisConvertParamBase param);
        public virtual double ValueToAxisValue(double value)
        {
            return ValueToAxisValue(value, GetValueAxisConvertParam());
        }
        /// <summary>
        /// 坐标值转换成实际值
        /// </summary>
        /// <param name="axisvalue"></param>
        /// <returns></returns>
        public abstract double AxisValueToValue(double axisvalue, ValueAxisConvertParamBase param);
        public virtual double AxisValueToValue(double value)
        {
            return AxisValueToValue(value, GetValueAxisConvertParam());
        }

        /// <summary>
        /// 获取实际值转化成坐标值的参数，用于GetLocation()
        /// </summary>
        /// <returns></returns>
        public virtual ValueLocationConvertParam GetConvertParam()
        {
            var p = GetValueAxisConvertParam();
            return new ValueLocationConvertParam()
            {
                MaxAxisValue = ValueToAxisValue(Max, p),
                MinAxisValue = ValueToAxisValue(Min, p),
                Length = AxisType == AxisType.X ? this.ActualWidth : this.ActualHeight,
                AxisType = AxisType,
                ValueAxisParam = p
            };
        }
        /// <summary>
        /// 获取真实数字对应的位置
        /// </summary>
        /// <param name="value">真实数据值</param>
        /// <returns></returns>
        public virtual double GetValueLocation(double value)
        {
            if (!IsLoaded) return default;
            return GetValueLocation(value, this.GetConvertParam());
        }
        /// <summary>
        /// 获取真实数据对应的坐标位置
        /// </summary>
        /// <param name="value">真实数据值</param>
        /// <param name="convert">转化参数，GetConvertParam()获取</param>
        /// <returns></returns>
        public virtual double GetValueLocation(double value, ValueLocationConvertParam convert)
        {
            value = ValueToAxisValue(value, convert.ValueAxisParam);
            return GetAxisValueLocation(value, convert);
        }
        /// <summary>
        /// 获取坐标数字对应的位置
        /// </summary>
        /// <param name="value">坐标数据</param>
        /// <returns></returns>
        public virtual double GetAxisValueLocation(double value)
        {
            if (!IsLoaded) return default;
            return GetAxisValueLocation(value, this.GetConvertParam());
        }
        /// <summary>
        /// 获取坐标数据对应的坐标位置
        /// </summary>
        /// <param name="value">数据值--xy轴坐标上的值</param>
        /// <param name="convert">转化参数，GetConvertParam()获取</param>
        /// <returns></returns>
        public virtual double GetAxisValueLocation(double value, ValueLocationConvertParam convert)
        {
            var temp = (value - convert.MinAxisValue) * convert.Length / (convert.MaxAxisValue - convert.MinAxisValue);
            return convert.AxisType == AxisType.X ? temp : convert.Length - temp;
        }
        /// <summary>
        /// 获取点位对应的坐标值
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public virtual double GetLocationAxisValue(double value)
        {
            return GetLocationAxisValue(value, GetConvertParam());
        }
        /// <summary>
        /// 获取点位对应的坐标值
        /// </summary>
        /// <param name="value"></param>
        /// <param name="convert"></param>
        /// <returns></returns>
        public virtual double GetLocationAxisValue(double value, ValueLocationConvertParam convert)
        {
            return (convert.AxisType == AxisType.X ? value : convert.Length - value) * (convert.MaxAxisValue - convert.MinAxisValue) / convert.Length + convert.MinAxisValue;
        }
        /// <summary>
        /// 获取点位对应的实际值
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public virtual double GetLocationValue(double value)
        {
            return GetLocationValue(value, GetConvertParam());
        }
        /// <summary>
        /// 获取点位对应的实际值
        /// </summary>
        /// <param name="value"></param>
        /// <param name="convert"></param>
        /// <returns></returns>
        public virtual double GetLocationValue(double value, ValueLocationConvertParam convert)
        {
            return AxisValueToValue(GetLocationAxisValue(value, convert), convert.ValueAxisParam);
        }
        #endregion

        #region 图形绘制
        protected static Typeface DefaultTypeface { get; } = new Typeface("Segoe UI");
        protected static NumberSubstitution DefaultNumberSubstitution { get; } = new NumberSubstitution();
        protected static Point PointZero { get; } = new Point(0, 0);
        protected Pen LinePen { get; set; } = new Pen(Brushes.Black, 1d);
        protected Pen StringPen { get; set; } = new Pen(Brushes.Black, 0.1d);

        /// <summary>
        /// 绘制轴的显示
        /// </summary>
        /// <returns></returns>
        public abstract void Draw();

        /// <summary>
        /// 发生变化
        /// </summary>
        public Action AxisChanged { get; set; }
        /// <summary>
        /// 重新绘制所有内容，关键属性发生变化时
        /// </summary>
        public virtual void RedrawAll()
        {
            Draw();
            if (MoveStartPoint == default) AxisChanged?.Invoke();
        }
        protected virtual double GetSubRange(double range, int count)
        {
            var subrange = Math.Pow(10, Math.Floor(Math.Log(range / count) / Math.Log(10)));
            var residual = range / count / subrange;
            if (residual > 5) subrange = 10 * subrange;
            else if (residual > 2) subrange = 5 * subrange;
            else if (residual > 1) subrange = 2 * subrange;
            return subrange;
        }
        #endregion

        #region 放大缩小移动
        /// <summary>
        /// 轴移动事件
        /// </summary>
        public Action<double> AxisMoved { get; set; }
        protected Point MoveStartPoint { get; set; }
        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonDown(e);
            MoveStartPoint = e.GetPosition(this);
        }
        protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonUp(e);
            if (MoveStartPoint != default)
            {
                MoveStartPoint = default;
                AxisChanged?.Invoke();
            }
        }

        protected override void OnMouseLeave(MouseEventArgs e)
        {
            base.OnMouseLeave(e);
            if (MoveStartPoint != default)
            {
                MoveStartPoint = default;
                AxisChanged?.Invoke();
            }
        }
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (e.LeftButton == MouseButtonState.Pressed && MoveStartPoint != default)
            {
                var point = e.GetPosition(this);
                var x = point.X - MoveStartPoint.X;
                var y = point.Y - MoveStartPoint.Y;
                MoveStartPoint = point;
                Move(x, y);
            }
        }

        protected override void OnMouseWheel(MouseWheelEventArgs e)
        {
            base.OnMouseWheel(e);
            Zoom(e.Delta > 0 ? 1.1d : 0.9d, e.GetPosition(this));
        }

        protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
        {
            base.OnRenderSizeChanged(sizeInfo);
            RedrawAll();
        }

        /// <summary>
        /// 鼠标移动
        /// </summary>
        /// <param name="x">x轴移动距离</param>
        /// <param name="y">y轴移动距离</param>
        internal virtual void Move(double x, double y)
        {
            if (!IsLoaded) return;
            double controlActualLength = 1;
            double movelength = 0d;
            switch (AxisType)
            {
                case AxisType.X:
                    controlActualLength = this.ActualWidth;
                    movelength = x;
                    AxisMoved?.Invoke(x);
                    break;
                case AxisType.Y:
                    controlActualLength = this.ActualHeight;
                    movelength = -y;//纵坐标的方向在图形显示和轴上是反向的
                    AxisMoved?.Invoke(y);
                    break;
                case AxisType.Y2:
                    controlActualLength = this.ActualHeight;
                    movelength = -y;//纵坐标的方向在图形显示和轴上是反向的
                    AxisMoved?.Invoke(y);
                    break;
                default:
                    break;
            }
            var p = GetValueAxisConvertParam();
            var MaxAxis = ValueToAxisValue(Max, p);
            var MinAxis = ValueToAxisValue(Min, p);
            var temp = (MaxAxis - MinAxis) * movelength / controlActualLength;
            var max = AxisValueToValue(MaxAxis - temp, p);
            var min = AxisValueToValue(MinAxis - temp, p);
            SetRange(max, min);
        }

        internal virtual void SetRange(double max, double min)
        {
            Range = new AxisRange() { Max = (double.IsNaN(max) || double.IsInfinity(max)) ? Range.Max : max, Min = (double.IsNaN(min) || double.IsInfinity(min)) ? Range.Min : min };
        }

        /// <summary>
        /// 鼠标滚轮放大缩小
        /// </summary>
        /// <param name="value">放大缩小比例</param>
        /// <param name="point">中心点位</param>
        internal virtual void Zoom(double percent, Point point)
        {
            if (!IsLoaded) return;
            double controlLength = 1;
            double pv = 0d;
            switch (AxisType)
            {
                case AxisType.X:
                    controlLength = this.ActualWidth;
                    pv = point.X;
                    break;
                case AxisType.Y:
                    controlLength = this.ActualHeight;
                    pv = this.ActualHeight - point.Y;
                    break;
                case AxisType.Y2:
                    controlLength = this.ActualHeight;
                    pv = this.ActualHeight - point.Y;
                    break;
                default:
                    break;
            }
            var p = GetValueAxisConvertParam();
            var MaxAxis = ValueToAxisValue(Max, p);
            var MinAxis = ValueToAxisValue(Min, p);
            percent = 1 / percent;
            var v = (MaxAxis - MinAxis) * (pv / controlLength) + MinAxis;
            var max = AxisValueToValue(v + (MaxAxis - v) * percent, p);
            var min = AxisValueToValue(v - (v - MinAxis) * percent, p);
            SetRange(max, min);
        }
        #endregion
    }

    /// <summary>
    /// 轴类型
    /// </summary>
    public enum AxisType
    {
        X,
        Y,
        Y2
    }
    /// <summary>
    /// 轴转换参数，跨线程使用
    /// </summary>
    public class ValueLocationConvertParam
    {
        /// <summary>
        /// 最大坐标值
        /// </summary>
        public double MaxAxisValue { get; set; }
        /// <summary>
        /// 最小坐标值
        /// </summary>
        public double MinAxisValue { get; set; }
        /// <summary>
        /// 轴图形长度
        /// </summary>
        public double Length { get; set; }
        /// <summary>
        /// 轴类型
        /// </summary>
        public AxisType AxisType { get; set; }
        /// <summary>
        /// 实际值和坐标值转换参数
        /// </summary>
        public ValueAxisConvertParamBase ValueAxisParam { get; set; }
    }

    /// <summary>
    /// 实际值和坐标值转换参数
    /// </summary>
    public class ValueAxisConvertParamBase
    {

    }

    /// <summary>
    /// 轴距范围
    /// </summary>
    public struct AxisRange
    {
        public double Min { get; set; }
        public double Max { get; set; }
    }
}
